#ifndef LISTA_ENC_H_
#define LISTA_ENC_H_
//Classe Lista Encadeada

#include <iostream>
using namespace std;

namespace miniprojeto {
namespace util {

template <class TEL>
class Lista_Enc
{
public:
   Lista_Enc(); //construtor padrão
   Lista_Enc(Lista_Enc<TEL>&); //construtor por cópia
   
   void InsereFim(const TEL&);
   void InsereInicio(const TEL&);
   bool Remove(const TEL&);
   bool RemoveInicio();
   bool RemoveFim();
   bool Primeiro(TEL&);
   bool Proximo(TEL&);
   bool Vazia();
   void ApagaLista();
   void Imprime();
   bool Procura(const TEL&);
   void setposCorrente(const TEL&);
   int getTamanho();
   TEL getposCorrente();
   Lista_Enc<TEL>& operator=(Lista_Enc<TEL>&);
   
protected:
   struct No;
   typedef No* pontNo;
   
   struct No
   {
      TEL Info;
      pontNo Lig;//ponteiro para o próximo Nó
   };
   
   pontNo Inicio, Fim, posCorrente;
   int Tamanho;
   
   pontNo Localiza (const TEL&);
};


template <class TEL>
Lista_Enc<TEL>::Lista_Enc()
{ 
   posCorrente = Inicio = Fim = 0;
   Tamanho = 0;
}

template <class TEL>
void Lista_Enc<TEL>::InsereFim(const TEL& Elemento)
{
     pontNo novoNo = new No;
     novoNo->Info = Elemento;
     if (Vazia())
     {
        Inicio = Fim = novoNo;
        novoNo->Lig = 0;
     }
     else
     {
         Fim->Lig = novoNo;
         Fim = novoNo;
         novoNo->Lig = 0;
     }
	 posCorrente = novoNo;
	 Tamanho++;
}

template <class TEL>
void Lista_Enc<TEL>::InsereInicio(const TEL& Elemento)
{
     pontNo novoNo = new No;
     novoNo->Info = Elemento;
     if (Vazia())
     {
        Inicio = Fim = novoNo;
        novoNo->Lig = 0;
     }
     else
     {
         novoNo->Lig = Inicio;
         Inicio = novoNo;;
     }
	 posCorrente = novoNo;
	 Tamanho++;
}

template <class TEL>
bool Lista_Enc<TEL>::Remove(const TEL& Elemento)
{
     if (Vazia())
       return false;
       
     if (Elemento == Inicio->Info)
        return RemoveInicio();
     
     pontNo Aux = Inicio;
        
     while (Aux->Lig != 0)
     {
         if (Aux->Lig->Info == Elemento)
         {
             pontNo Aux2 = Aux->Lig;
             Aux->Lig = Aux2->Lig;   
             delete Aux2;
             if (Aux->Lig == 0)
                Fim = Aux;
			 posCorrente = Aux;
             
			 Tamanho--;   
             return true;
         }
         Aux = Aux->Lig;
     }
     return false;
}

template <class TEL>
bool Lista_Enc<TEL>::RemoveInicio()
{
     if (Vazia())
       return false;
     
     pontNo Aux = Inicio;
     Inicio = Inicio->Lig;
     if (Fim == Aux)
           Fim = posCorrente = 0;
           
     delete Aux;
	 Tamanho--;
     return true;
}

template <class TEL>
bool Lista_Enc<TEL>::RemoveFim()
{
     if (Vazia())
       return false;


	 if (Inicio == Fim)
	 {
		 delete Fim;
		 Inicio = Fim = 0;
	 }
	 else
	 {
		 pontNo Aux = Inicio;
		 while (Aux->Lig != Fim)
			 Aux = Aux->Lig;
		 Fim = Aux;
		 delete Aux->Lig;
		 Aux->Lig = 0;
	 }
	 Tamanho--;
	return true;
}

template <class TEL>
bool Lista_Enc<TEL>::Primeiro(TEL& Elemento)
{
     if (Vazia())
       return false;
     
     Elemento = Inicio->Info;
     posCorrente = Inicio;
     return true;
}

template <class TEL>
bool Lista_Enc<TEL>::Proximo(TEL& Elemento)
{
     if (Vazia() || posCorrente->Lig == 0)
       return false;
       
     posCorrente = posCorrente->Lig;
     Elemento = posCorrente->Info;
     return true;
}

template <class TEL>
bool Lista_Enc<TEL>::Vazia()
{
     return (Inicio==0);
}

template <class TEL>
void Lista_Enc<TEL>::ApagaLista()
{
     pontNo Aux;
     while (Inicio != 0)
     {
        Aux = Inicio;
        Inicio = Inicio->Lig;
        delete Aux;
     }
	 posCorrente = Fim = 0;
	 Tamanho = 0;
}

template <class TEL>
bool Lista_Enc<TEL>::Procura (const TEL& Elemento)
{
      if (Vazia())
         return 0;
      
      if (Inicio->Info == Elemento)
	  {
		  posCorrente = Inicio;
		  return true;
	  }
        
      if (Fim->Info == Elemento)
	  {
		  posCorrente = Fim;
          return true;
	  }
        
      pontNo Aux = Inicio->Lig;
      while (Aux != Fim)
      {
         if (Aux->Info == Elemento)
		 {
			 posCorrente = Aux;
             return true;
		 }
         Aux = Aux->Lig;
      }

      return false;
}

template <class TEL>
void Lista_Enc<TEL>::Imprime()
{
	TEL Aux;
	if (Primeiro(Aux))
	{
		cout<<Aux;
		while (Proximo(Aux))
			cout<<" "<<Aux;
	}
	else
		cout<<"Lista Vazia";
	cout<<endl;
}

template <class TEL>
Lista_Enc<TEL>& Lista_Enc<TEL>::operator=(Lista_Enc<TEL>& L)
{
    if ((this != &L))//evitar auto atribuição
    {
       ApagaLista();
       if (!L.Vazia())//se fazia, a lista esta igual
       {
         TEL Aux;
         L.Primeiro(Aux);
		 InsereFim(Aux);
         while (L.Proximo(Aux))
		    InsereFim(Aux);
       }
    }
    return *this; //atribuição em cadeia
}

template <class TEL>
Lista_Enc<TEL>::Lista_Enc(Lista_Enc<TEL>& L)
{
	Inicio = Fim = posCorrente = 0;
	Tamanho = 0;
	if (!L.Vazia())//se fazia, a lista esta igual
    {
		TEL Aux;
        L.Primeiro(Aux);
		InsereFim(Aux);
        while (L.Proximo(Aux))
		  InsereFim(Aux);
    }
	else
		ApagaLista();
}

template <class TEL>
typename Lista_Enc<TEL>::pontNo Lista_Enc<TEL>::Localiza (const TEL& Elemento)
{
      if (Vazia())
         return 0;
      
      if (Inicio->Info == Elemento)
        return Inicio;
        
      if (Fim->Info == Elemento)
        return Fim;
        
      pontNo Aux = Inicio->Lig;
      while (Aux != Fim)
      {
         if (Aux->Info == Elemento)
            return Aux;
            
         Aux = Aux->Lig;
      }

      return 0;   struct No;
   typedef No* pontNo;
   
   struct No
   {
      TEL Info;
      pontNo Lig;//ponteiro para o próximo Nó
   };
   
   pontNo Inicio, Fim, posCorrente;
   int Tamanho;
   
   pontNo Localiza (const TEL&);
}

template <class TEL>
int Lista_Enc<TEL>::getTamanho()
{
	return Tamanho;
}

}
}
#endif /*LISTA_ENC_H_*/
